package se.webbzon.oschi01.aes;

import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;

public class RijndaelBuilder implements DataOutput {
	
	// The content of this RijndaelBuilder
	private final ByteArrayOutputStream content;
	
	// The data output stream used write into the content
	private final DataOutputStream dos;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public RijndaelBuilder() {
		content = new ByteArrayOutputStream();
		dos = new DataOutputStream(content);
	}
	
	public RijndaelBuilder(int size) {
		content = new ByteArrayOutputStream(size);
		dos = new DataOutputStream(content);
	}

	/*============================================================
	Public Methods
	============================================================*/
	
	@Override public void write(int b) throws IOException {
		dos.write(b);
	}

	@Override public void write(byte[] b) throws IOException {
		dos.write(b);
	}

	@Override public void write(byte[] b, int off, int len) throws IOException {
		dos.write(b, off, len);
	}

	@Override public void writeBoolean(boolean v) throws IOException {
		dos.writeBoolean(v);
	}

	@Override public void writeByte(int v) throws IOException {
		dos.writeByte(v);
	}

	@Override public void writeBytes(String s) throws IOException {
		dos.writeBytes(s);
	}

	@Override public void writeChar(int v) throws IOException {
		dos.writeChar(v);
	}

	@Override public void writeChars(String s) throws IOException {
		dos.writeChars(s);
	}

	@Override public void writeDouble(double v) throws IOException {
		dos.writeDouble(v);
	}

	@Override public void writeFloat(float v) throws IOException {
		dos.writeFloat(v);
	}

	@Override public void writeInt(int v) throws IOException {
		dos.writeInt(v);
	}

	@Override public void writeLong(long v) throws IOException {
		dos.writeLong(v);
	}

	@Override public void writeShort(int v) throws IOException {
		dos.writeShort(v);
	}

	@Override public void writeUTF(String str) throws IOException {
		dos.writeUTF(str);
	}
	
	
	/** Writes the content of this RijndaelBuilder into the provided
	 * byte array. **/
	public void writeTo(byte[] b, int offset) {
		int size = content.size();
		b[offset] = toUTF8Hex(15 - size % 16);
		ByteBufferOutputStream os = 
				new ByteBufferOutputStream(ByteBuffer.wrap(b, offset+1, size));
		try {
			content.writeTo(os);
		} catch (IOException e) {
			throw new ArrayStoreException();
		}
	}
	
	/** Returns the current size of the content in this RijndaelBuilder. **/
	public int size() {
		return 16 * (1 + (1 + content.size()) / 16); 
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Returns the byte code of the hexadecimal UTF-8 character
	 * representing the given value. **/
	private byte toUTF8Hex(int i) {
		if (i > 9)
			return  (byte) (i + 87);
		else
			return (byte) (i + 48);
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	/** An output stream used to write into a byte buffer. **/
	private static final class ByteBufferOutputStream extends OutputStream {
		
		// The byte buffer to write the data into
		private final ByteBuffer buffer;
		
		/*============================================================
		Constructors
		============================================================*/
		
		public ByteBufferOutputStream(ByteBuffer buffer) {
			this.buffer = buffer;
		}
		
		/*============================================================
		Public Methods
		============================================================*/
		
		@Override public void write(int b) throws IOException {
			buffer.put((byte) b);
		}
		
		@Override public void write(byte[] b, int offset, int length) {
			buffer.put(b, offset, length);
		}
		
	}
	
}
